package reports;

import static org.junit.Assert.*;

import java.text.ParseException;
import java.util.Date;
import java.util.Iterator;

import model.entity.Product;

import org.junit.Test;

import reports.ProductStatisticsReportTestSuperclass.MockNode;
import reports.visitor.IVisitor;
import common.util.DateUtils;

/**
 * @author Trevor Dixon, Ian Ryu
 *
 */
public class ProductStatisticsReportProductsTest extends ProductStatisticsReportTestSuperclass
{
	private static int countIterations(Iterator<?> i)
	{
		int n = 0;
		for (; i.hasNext(); ++n)
			i.next();
		return n;
	}

	@Test
	public void testNumberProducts() throws ParseException
	{
		ProductStatisticsReport report;

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a. There are no products in the system
		// //////////////////////////////////////////////////////////////////////////////////////////

		report = generateReport(1);
		assertEquals(countIterations(report.getProducts()), 0);

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a. There lots of products in the system
		// //////////////////////////////////////////////////////////////////////////////////////////

		final int fakeProducts = 59;
		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{
				for (int i = 0; i < fakeProducts; i++)
				{
					generateProduct().preOrderAccept(visitor);
				}
			}
		});

		report = generateReport(1);
		assertEquals(countIterations(report.getProducts()), fakeProducts);
	}

	@Test
	public void testAddedDuring() throws ParseException
	{
		ProductStatisticsReport report;

		final Date firstDay = DateUtils.parseDate("03/01/2013");
		final Date middleDay = DateUtils.parseDate("04/15/2013");
		final Date lastDay = DateUtils.parseDate("06/01/2013");

		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{
				Date today = DateUtils.currentDate();

				// Product added on first day of the reporting period
				DateUtils.setCurrentDate(firstDay);
				generateProduct().preOrderAccept(visitor);

				// Product added in the middle of the reporting period
				DateUtils.setCurrentDate(middleDay);
				generateProduct().preOrderAccept(visitor);

				// Product added on last day of the reporting period
				DateUtils.setCurrentDate(lastDay);
				generateProduct().preOrderAccept(visitor);

				DateUtils.setCurrentDate(today);
			}
		});

		DateUtils.setCurrentDate(lastDay);
		report = generateReport(3);

		assertEquals(countIterations(report.getProducts()), 3);
	}

	@Test
	public void testAddedBefore() throws ParseException
	{
		ProductStatisticsReport report;

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a product initially added before the reporting period
		// //////////////////////////////////////////////////////////////////////////////////////////


		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{

				try {
					Date today = DateUtils.parseDate("05/16/2013");
					DateUtils.setCurrentDate(today);
					System.out.println("1:" +DateUtils.currentDate());

					Date creationDate = DateUtils.parseDate("05/16/2010");
					DateUtils.setCurrentDate(creationDate);
					System.out.println("2:" +DateUtils.currentDate());
					for (int i = 0; i < 1; i++)
					{
						generateProduct().preOrderAccept(visitor);
						System.out.println("createionDate: "+generateProduct().getCreationDate());
					}

					DateUtils.setCurrentDate(today);
					System.out.println("3:" +DateUtils.currentDate());
				} catch (ParseException e) {
					fail(e.getMessage());
				}

			}
		});

		report = generateReport(1);
		assertEquals(countIterations(report.getProducts()), 1);

	}

	@Test
	public void testUpdatedDuring() throws ParseException
	{
		ProductStatisticsReport report;

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a product initially updated during the reporting period
		// //////////////////////////////////////////////////////////////////////////////////////////


		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{

				try {
					Date today = DateUtils.parseDate("05/16/2013");
					DateUtils.setCurrentDate(today);

					Date creationDate = DateUtils.parseDate("02/16/2013");
					DateUtils.setCurrentDate(creationDate);
					for (int i = 0; i < 1; i++)
					{
						Product product = generateProduct();
						product.setDescription("updated");
						product.preOrderAccept(visitor);
					}

					DateUtils.setCurrentDate(today);
				} catch (ParseException e) {
					fail(e.getMessage());
				}

			}
		});

		report = generateReport(3);
		assertEquals(countIterations(report.getProducts()), 1);

	}

	@Test
	public void testUpdatedBefore() throws ParseException
	{
		ProductStatisticsReport report;

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a product initially updated before the reporting period
		// //////////////////////////////////////////////////////////////////////////////////////////


		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{

				try {
					Date today = DateUtils.parseDate("05/16/2012");
					DateUtils.setCurrentDate(today);

					Date creationDate = DateUtils.parseDate("02/16/2013");
					DateUtils.setCurrentDate(creationDate);
					for (int i = 0; i < 1; i++)
					{
						Product product = generateProduct();
						product.setDescription("updated");
						product.preOrderAccept(visitor);
					}

					DateUtils.setCurrentDate(today);
				} catch (ParseException e) {
					fail(e.getMessage());
				}

			}
		});

		report = generateReport(3);
		assertEquals(countIterations(report.getProducts()), 1);

	}

	@Test
	public void testChangedSameday() throws ParseException
	{
		ProductStatisticsReport report;

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a product initially changed same day of the reporting period
		// //////////////////////////////////////////////////////////////////////////////////////////


		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{

				try {
					Date today = DateUtils.parseDate("05/16/2013");
					DateUtils.setCurrentDate(today);

					Date creationDate = DateUtils.parseDate("05/16/2013");
					DateUtils.setCurrentDate(creationDate);
					for (int i = 0; i < 1; i++)
					{
						Product product = generateProduct();
						product.setDescription("changed");
						product.preOrderAccept(visitor);
					}

					DateUtils.setCurrentDate(today);
				} catch (ParseException e) {
					fail(e.getMessage());
				}

			}
		});

		report = generateReport(3);
		assertEquals(countIterations(report.getProducts()), 1);
	}

	@Test
	public void testNoChanged() throws ParseException
	{
		ProductStatisticsReport report;

		// //////////////////////////////////////////////////////////////////////////////////////////
		// a product nothing changed during the reporting period
		// //////////////////////////////////////////////////////////////////////////////////////////


		setProductManagerVisitorNode(new MockNode()
		{
			@Override
			public void preOrderAccept(IVisitor visitor)
			{

				try {
					Date today = DateUtils.parseDate("04/16/2013");
					DateUtils.setCurrentDate(today);

					Date creationDate = DateUtils.parseDate("05/16/2013");
					DateUtils.setCurrentDate(creationDate);
					for (int i = 0; i < 1; i++)
					{
						Product product = generateProduct();
						product.preOrderAccept(visitor);
					}

					DateUtils.setCurrentDate(today);
				} catch (ParseException e) {
					fail(e.getMessage());
				}

			}
		});

		report = generateReport(3);
		assertEquals(countIterations(report.getProducts()), 1);
	}


	public void testItemsAddedDifferentTime()
	{
		try
		{
			ProductStatisticsReport report;

			final Date today = DateUtils.parseDate("03/31/2013");
			DateUtils.setCurrentDate(today);

			setItemManagerVisitorNode(new MockNode()
			{
				@Override
				public void preOrderAccept(IVisitor visitor)
				{
					try
					{
						// generate 1 item one day after end of report period
						Date entryDate = DateUtils.parseDate("04/01/2013");
						generateItem(entryDate).preOrderAccept(visitor);  

						// generate 2 items on last day of report period
						for (int i = 0; i < 2; i++)
							generateItem(today).preOrderAccept(visitor);

						// generate 3 items one day before end of report period
						entryDate = DateUtils.parseDate("03/30/2012");
						for (int i = 0; i < 3; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// four month ago

						// generate 4 items four month before end of report period, less one day
						entryDate = DateUtils.parseDate("12/01/2012");
						for (int i = 0; i < 4; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// generate 5 items four month before end of report period
						entryDate = DateUtils.parseDate("11/30/2012");
						for (int i = 0; i < 5; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// generate 6 items four month and one day before end of report period
						entryDate = DateUtils.parseDate("11/29/2012");
						for (int i = 0; i < 6; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// 12 months ago

						// generate 7 items 11 months before end of report period, less one month
						entryDate = DateUtils.parseDate("04/30/2012");
						for (int i = 0; i < 7; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// generate 8 items 12 months before end of report period
						entryDate = DateUtils.parseDate("03/31/2012");
						for (int i = 0; i < 8; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// generate 9 items 13 months before end of report period
						entryDate = DateUtils.parseDate("02/29/2012");
						for (int i = 0; i < 9; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// 100 months ago

						// generate 10 items 100 months before end of report period, less one day
						entryDate = DateUtils.parseDate("10/01/2003");
						for (int i = 0; i < 10; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// generate 11 items 100 months before end of report period
						entryDate = DateUtils.parseDate("09/30/2003");
						for (int i = 0; i < 11; i++)
							generateItem(entryDate).preOrderAccept(visitor);

						// generate 12 items 100 months and one day before end of report period
						entryDate = DateUtils.parseDate("09/29/2003");
						for (int i = 0; i < 12; i++)
							generateItem(entryDate).preOrderAccept(visitor);
					}
					catch (ParseException e)
					{
						fail(e.getMessage());
					}
				}
			});

			setProductManagerVisitorNode(new MockNode()
			{
				@Override
				public void preOrderAccept(IVisitor visitor)
				{
					getDefaultProduct().preOrderAccept(visitor);
				}
			});

			Product product = getDefaultProduct();

			// //////////////////////////////////////////////////////////////////////////////////////////
			// a. One Month Report (32 days)
			// //////////////////////////////////////////////////////////////////////////////////////////

			report = generateReport(1);
			int total = 77;
			int added = 2;
			int addedOnFirstDay = 0;
			int minSupply = total - added + addedOnFirstDay;
			int maxAge = 3471;

			assertEquals(total, report.getCurrentSupply(product));
			assertEquals(75.0625, report.getAverageSupply(product), 0.1f);

			assertEquals(minSupply, report.getMinSupply(product));
			assertEquals(total, report.getMaxSupply(product));

			assertEquals(0, report.getQuantityUsed(product));
			assertEquals(added, report.getQuantityAdded(product));

			assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
			assertEquals(0, report.getMaxUsedAge(product));

			assertEquals(1639.6883f, report.getAverageCurrentAge(product), 0.1f);
			assertEquals(maxAge, report.getMaxCurrentAge(product));
		}
		catch (ParseException e)
		{
			fail(e.getMessage());
		}
	}
	
}
